package model

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"gitlab.local/backend/pkg/utils"
)

type Packet struct {
	Magic uint8
	Len   uint8
	ID    uint8
	Cmd   uint8
	Body  []byte
	Crc   uint8
}

func BuildPacket(cmd CmdType, id uint8, data []byte) *Packet {
	p := &Packet{
		Magic: Magic,
		Cmd:   uint8(cmd),
		ID:    id,
		Len:   uint8(len(data) + 2 + 1),
		Body:  data,
	}

	crcData := p.ToHexBytes(true)
	p.Crc = utils.Getcrc8(crcData)
	return p
}

func minLength() int {
	return 1 + 1 + 1 + 1 + 1
}

func Compare(b1, b2 *Packet) (bool, error) {

	if b1.Magic != b2.Magic {
		return false, fmt.Errorf("magic mismatch")
	}
	if b1.Len != b2.Len {
		return false, fmt.Errorf("length mismatch")
	}
	if b1.ID != b2.ID {
		return false, fmt.Errorf("id mismatch")
	}
	if b1.Cmd != b2.Cmd {
		return false, fmt.Errorf("cmd mismatch")
	}
	if !bytes.Equal(b1.Body, b2.Body) {
		return false, fmt.Errorf("body mismatch")
	}
	if b1.Crc != b2.Crc {
		return false, fmt.Errorf("crc mismatch")
	}
	return true, nil
}

func CheckCrc(b []byte) bool {

	//fmt.Printf("%X\n", b)
	//fmt.Printf("%X\n", b[:len(b)-2])
	//fmt.Printf("%X\n", b[len(b)-1])
	//fmt.Printf("%X\n", utils.Getcrc8(b[:len(b)-1]))
	return utils.Getcrc8(b[:len(b)-1]) == b[len(b)-1]
}

func BytesToObject(b []byte) interface{} {

	if len(b) < minLength() {
		return nil
	}

	var body []byte
	if len(b) > minLength() {
		bodyLen := len(b) - minLength()
		body = b[4 : 4+bodyLen]
	}

	p := &Packet{
		Magic: b[0],
		Len:   b[1],
		ID:    b[2],
		Cmd:   b[3],
		Body:  body,
		Crc:   b[len(b)-1],
	}
	return p
}

func (p *Packet) ToHexBytes(args ...any) []byte {
	var (
		crcCalculate bool
		buffer                        = bytes.Buffer{}
		order        binary.ByteOrder = binary.BigEndian
	)
	if len(args) == 1 {
		crcCalculate = args[0].(bool)
	}

	_ = binary.Write(&buffer, order, p.Magic)
	_ = binary.Write(&buffer, order, p.Len)
	_ = binary.Write(&buffer, order, p.ID)
	_ = binary.Write(&buffer, order, p.Cmd)
	buffer.Write(p.Body)
	if !crcCalculate {
		_ = binary.Write(&buffer, binary.BigEndian, p.Crc)
	}

	return buffer.Bytes()
}

type LoginResponse struct {
	Step uint8
	Body []byte
}

func (p *LoginResponse) ToHexBytes() []byte {
	var buffer bytes.Buffer

	_ = binary.Write(&buffer, binary.BigEndian, p.Step)
	buffer.Write(p.Body)

	return buffer.Bytes()
}

type PlayerVoice struct {
	Id    uint8
	Count uint8
	Time  uint8
}

func (p *PlayerVoice) ToHexBytes() []byte {
	var buffer bytes.Buffer

	_ = binary.Write(&buffer, binary.BigEndian, p.Id)
	_ = binary.Write(&buffer, binary.BigEndian, p.Count)
	_ = binary.Write(&buffer, binary.BigEndian, p.Time)
	return buffer.Bytes()
}

type AlarmConfig struct {
	SilentStartTime      uint8
	SilenceStopTime      uint8
	VibrationSensitivity uint8
}

func (ac *AlarmConfig) ToHexBytes() []byte {
	var (
		buffer bytes.Buffer
	)

	_ = binary.Write(&buffer, binary.LittleEndian, ac.SilentStartTime)
	_ = binary.Write(&buffer, binary.LittleEndian, ac.SilenceStopTime)
	_ = binary.Write(&buffer, binary.LittleEndian, ac.VibrationSensitivity)
	return buffer.Bytes()
}

type SmartConfig struct {
	LoseControlEnable   bool // 放飞车
	WalkAssistEnable    bool // 步行模式
	HillAssistEnable    bool // 上坡辅助
	RssiPowerOnEnable   bool // 无感启动
	TimedPowerOffEnable bool // 离车自动锁车

	LoseControl   uint8
	WalkAssist    uint8
	HillAssist    uint8
	RssiPowerOn   uint8
	TimedPowerOff uint8
}

func (sc *SmartConfig) ToHexBytes() []byte {
	var (
		buffer  bytes.Buffer
		enabled = make([]byte, 1)
		w       = utils.NewBitWriter(enabled)
	)

	// bit 7
	w.WriteBit(0)
	// bit 6
	w.WriteBit(0)
	// bit 5
	w.WriteBit(0)

	// bit 4
	if sc.TimedPowerOffEnable {
		w.WriteBit(1)
	} else {
		w.WriteBit(0)
	}

	// bit 3
	if sc.RssiPowerOnEnable {
		w.WriteBit(1)
	} else {
		w.WriteBit(0)
	}

	// bit 2
	if sc.HillAssistEnable {
		w.WriteBit(1)
	} else {
		w.WriteBit(0)
	}

	// bit 1
	if sc.WalkAssistEnable {
		w.WriteBit(1)
	} else {
		w.WriteBit(0)
	}

	// bit 0
	if sc.LoseControlEnable {
		w.WriteBit(1)
	} else {
		w.WriteBit(0)
	}

	_ = binary.Write(&buffer, binary.LittleEndian, enabled)
	_ = binary.Write(&buffer, binary.LittleEndian, sc.LoseControl)
	_ = binary.Write(&buffer, binary.LittleEndian, sc.WalkAssist)
	_ = binary.Write(&buffer, binary.LittleEndian, sc.HillAssist)
	_ = binary.Write(&buffer, binary.LittleEndian, sc.RssiPowerOn)
	_ = binary.Write(&buffer, binary.LittleEndian, sc.TimedPowerOff)

	return buffer.Bytes()
}

type ReportData struct {
	// ****** location data  ******
	// 0x51 data
	Time uint32 //时间戳
	// 0x71 data
	Lat      uint32 // 纬度
	Lng      uint32 // 经度
	GpsSpeed uint16 // GPS速度
	Bearing  uint16 // gps方向角

	// 0x72 data
	Speed uint8 // 车速

	// 0x73 data
	IsQuantity    uint8
	Electricity   uint16 // 电量，有BMSbit15=0报百分比，无BMSbit15=1报电压，单位：0.1V
	Mileage       uint32 // 总里程
	BatteryStatus uint8  // 电池状态 0：静置（Idle）1：充电（Charge）2：放电（Discharge）3：回馈（Feedback Charge）4：满充（Full Charge）

	// ****** signal data  ******
	// 0x74 data
	Signal4G  uint8
	SignalGps uint8

	// ****** alarm data  ******
	// 0x76 data
	Shock               uint8 // 震动
	Rotation            uint8 // 轮动
	Pressure            uint8 // 压力
	Dump                uint8 // 倾倒 （加速度计）
	LowPower            uint8 // 低电量
	BatteryDisconnected uint8 // 电瓶断开
	NoGps               uint8 // 长时间无GPS定位

	// ****** power status data  ******
	// 0x61 data
	PowerStatus uint8 // device is power on
	LockStatus  uint8 // device is locked

	// ****** switch bits is enable  ******
	// 0x62 data
	AccEnable           bool
	SwitchEnable        bool
	DisarmEnable        bool
	DirectionLockEnable bool
	BatteryLockEnable   bool
	HelmetLockEnable    bool
	// ****** switch bits ******
	Acc         uint8
	Switch      uint8
	Disarm      uint8
	Direction   uint8
	BatteryLock uint8
	HelmetLock  uint8

	// 0x52 data
	// 上锁态：0x01：休眠模式 0x02：待机模式
	// 解锁态：0x03：驻足模式 0x04：骑行模式 0x05：助力模式
	RunMode uint8
	// 0x14 data
	FirmwareVersion string
}
